import java.io.*;
import javax.microedition.lcdui.Graphics;

class Frame {
	short nDx = 0;
	short nDy = 0;
	byte bDelay = 0;
	// shLayers[0] - PicID, 
	// shLayers[1] - Dx, 
	// shLayers[2] - Dy, 
	// shLayers[3] - visible
	long shLayers[];
	byte bOpt;
	byte pntX_1;
	byte pntY_1;
	byte pntX_2;
	byte pntY_2;

//    short nFrmLeft;
//    short nFrmTop;
	short nFrmRight;
	short nFrmBottom;
}

public class Animation {
	//------------------------------------------------------------------------------------------------------------------
	// static part
	private static Frame frmAnims[][];
	private static short shAnimsRect[][]; // left, top, w, h

	//------------------------------------------------------------------------------------------------------------------
	// dynamic part
	// constants
	static public final int cnANIM_STATE_VISIBLE = 1;
	static public final int cnANIM_STATE_PLAY_REVERSED = 2;
	static public final int cnANIM_STATE_PAUSED = 4;
	static public final int cnANIM_STATE_END_OF_CYCLE = 8;

	// members
	public short nStateFlg = cnANIM_STATE_VISIBLE;
	public short nAnimID;

	// timing in ticks
	public short nCurFrame;
	public short nCurTime;


	/**
	 *
	 */
	public Animation(int animID) {
		nAnimID = (short)animID;
	}

	/**
	 *
	 */
	public final void Reset() {
		nStateFlg &= ~(cnANIM_STATE_END_OF_CYCLE | cnANIM_STATE_PAUSED);
		if ((nStateFlg & cnANIM_STATE_PLAY_REVERSED) == 0) {
			nCurFrame = 0;
		}
		else {
			nCurFrame = (short)(frmAnims[nAnimID].length - 1);
		}
		nCurTime = 0;
	}

	/**
	 *
	 */
	public final void SetLastFrame() {
		if ((nStateFlg & cnANIM_STATE_PLAY_REVERSED) == 0)
			nCurFrame = (short)(frmAnims[nAnimID].length - 1);
		else
			nCurFrame = 0;
	}

	/**
	 *
	 */
	public final void SetFrame( int frm ) {
		if (frm < frmAnims[nAnimID].length) {
			if ((nStateFlg & cnANIM_STATE_PLAY_REVERSED) != 0)
				frm = frmAnims[nAnimID].length - frm;
			nCurFrame = (short) frm;
		}
	}


	/**
	 *
	 */
	public final void Resume() {
		nStateFlg &= ~cnANIM_STATE_PAUSED;
	}

	/**
	 *
	 */
	public final void Tick() {
		//Sprite update
		if ((nStateFlg & cnANIM_STATE_PAUSED) == 0) {
			nCurTime++;
			if ((nStateFlg & cnANIM_STATE_PLAY_REVERSED) == 0) {
				if (nCurTime >= frmAnims[nAnimID][nCurFrame].bDelay) {
					nCurTime = 0;
					if (++nCurFrame >= frmAnims[nAnimID].length) {
						nCurFrame = 0;
						nStateFlg |= cnANIM_STATE_END_OF_CYCLE;
					}
					else {
						nStateFlg &= ~cnANIM_STATE_END_OF_CYCLE;
					}
				}
			}
			else {
				if (nCurTime >= frmAnims[nAnimID][nCurFrame].bDelay) {
					nCurTime = 0;
					if (--nCurFrame < 0) {
						nCurFrame = (short)(frmAnims[nAnimID].length - 1);
						nStateFlg |= cnANIM_STATE_END_OF_CYCLE;
					}
					else {
						nStateFlg &= ~cnANIM_STATE_END_OF_CYCLE;
					}
				}
			}
		}
	}

	/**
	 *
	 */
	public short getLayerProperty(int lay, byte property) {
		if ((lay >= 0) && (lay < frmAnims[nAnimID][nCurFrame].shLayers.length)) {
			return (short) ((frmAnims[nAnimID][nCurFrame].shLayers[lay] >> (16 * (3 - property))) & 0x000000000000FFFF);
		}
		return 0;
	}

	/**
	 *
	 */
	public void setLayerProperty(int frame, int lay, byte property, int value) {
		if ((lay >= 0) && (lay < frmAnims[nAnimID][frame].shLayers.length)) {
			long props[] = new long[4];
			props[0] = ((frmAnims[nAnimID][frame].shLayers[lay] >> 48) & 0x000000000000FFFF);
			props[1] = ((frmAnims[nAnimID][frame].shLayers[lay] >> 32) & 0x000000000000FFFF);
			props[2] = ((frmAnims[nAnimID][frame].shLayers[lay] >> 16) & 0x000000000000FFFF);
			props[3] = (frmAnims[nAnimID][frame].shLayers[lay] & 0x000000000000FFFF);
			props[property] = value;

			frmAnims[nAnimID][frame].shLayers[lay] = ((props[0] << 48) & 0xFFFF000000000000L) |
													  ((props[1] << 32) & 0x0000FFFF00000000L) |
                                                      ((props[2] << 16) & 0x00000000FFFF0000L) |
                                                      (props[3] & 0x000000000000FFFFL);
		}
	}

	/**
	 *
	 */
	public void Draw(Graphics g, int x, int y) {
		Frame frm = frmAnims[nAnimID][nCurFrame];
		int lays = frm.shLayers.length;
		for (int i=0; i<lays; i++) {
			long p = frmAnims[nAnimID][nCurFrame].shLayers[i];
			if (((short)(p & 0x000000000000FFFF)) != 0 ) // isVisible()
				PicDrawer.DrawPic(g, 
				                  x+frm.nDx+((short)((p >> 32) & 0x000000000000FFFF)),
                                  y+frm.nDy+((short)((p >> 16) & 0x000000000000FFFF)),
                                  ((short)((p >> 48) & 0x000000000000FFFF)), 0);
		}
	}

	/**
	 *
	 */
	public void DrawAfter(Graphics g, int x, int y, Animation anim1, Animation anim2) {
		Frame frm = frmAnims[nAnimID][nCurFrame];
		int lays = frm.shLayers.length;
		int xx = x + frm.nDx;
		int yy = y + frm.nDy;

		for (int i=0; i<lays; i++) {
			long p = frmAnims[nAnimID][nCurFrame].shLayers[i];
			if (((short)(p & 0x000000000000FFFF)) != 0) // isVisible()
				PicDrawer.DrawPic(g, 
				                  x+frm.nDx+((short)((p >> 32) & 0x000000000000FFFF)),
                                  y+frm.nDy+((short)((p >> 16) & 0x000000000000FFFF)),
                                  ((short)((p >> 48) & 0x000000000000FFFF)), 0);
		}

		if (((frm.bOpt & 1) != 0) && (anim1 != null)) {
			anim1.Draw(g, xx + frm.pntX_1, yy + frm.pntY_1);
		}

		if (((frm.bOpt & 2) != 0) && (anim2 != null)) {
			anim2.Draw(g, xx + frm.pntX_2, yy + frm.pntY_2);
		}
	}

	/**
	 *
	 */
	public void DrawBefore(Graphics g, int x, int y, Animation anim1, Animation anim2) {
		Frame frm = frmAnims[nAnimID][nCurFrame];
		int lays = frm.shLayers.length;
		int xx = x + frm.nDx;
		int yy = y + frm.nDy;

		if (((frm.bOpt & 1) != 0) && (anim1 != null)) {
			anim1.Draw(g, xx + frm.pntX_1, yy + frm.pntY_1);
		}

		if (((frm.bOpt & 2) != 0) && (anim2 != null)) {
			anim2.Draw(g, xx + frm.pntX_2, yy + frm.pntY_2);
		}

		for (int i=0; i<lays; i++) {
			long p = frmAnims[nAnimID][nCurFrame].shLayers[i];
			if (((short)(p & 0x000000000000FFFF)) != 0) // isVisible()
				PicDrawer.DrawPic(g, 
				                  x + frm.nDx + ((short)((p >> 32) & 0x000000000000FFFF)),
                                  y + frm.nDy + ((short)((p >> 16) & 0x000000000000FFFF)),
                                  ((short)((p >> 48) & 0x000000000000FFFF)), 0);
		}
	}

	/**
	 *
	 */
	public int GetAnimWidth() {
		return shAnimsRect[nAnimID][0];
	}

	/**
	 *
	 */
	public int GetAnimHeight() {
		return shAnimsRect[nAnimID][1];
	}

	/**
	 *
	 */
	public int GetCurFrmWidth() {
		Frame frm = frmAnims[nAnimID][nCurFrame];
		return frm.nFrmRight;
	}

	/**
	 *
	 */
	public int GetCurFrmHeight() {
		Frame frm = frmAnims[nAnimID][nCurFrame];
		return frm.nFrmBottom;
	}

	/**
	 *
	 */
	public static void LoadAnims(String str_file) {
		DataInputStream dis;
		try {
			dis = new DataInputStream(MTMMidlet.the_class.getResourceAsStream( str_file ));
			if (dis != null) {
				int n_count = dis.readInt();
				frmAnims = new Frame[n_count][];
				shAnimsRect = new short[n_count][2];

				for (int i = 0; i < n_count; i++) {
					short n_frames = dis.readShort();

//               	shAnimsRect[i][0] = dis.readShort(); // x
//               	shAnimsRect[i][1] = dis.readShort(); // y
					dis.skipBytes(4);

//               	shAnimsRect[i][2] = dis.readShort(); // w
//               	shAnimsRect[i][3] = dis.readShort(); // h
					shAnimsRect[i][0] = dis.readShort(); // w
					shAnimsRect[i][1] = dis.readShort(); // h

					if (n_frames > 0) {
						frmAnims[i] = new Frame[n_frames];
						for (int f = 0; f < n_frames; f++) {
							Frame frm = frmAnims[i][f] = new Frame();

							// frame rect
//                     		frm.nFrmLeft = dis.readShort();
//                     		frm.nFrmTop = dis.readShort();
							dis.skipBytes(4);

							frm.nFrmRight = dis.readShort();
							frm.nFrmBottom = dis.readShort();
//                     		frm.nFrmBottom += frm.nFrmTop;
//                     		frm.nFrmRight += frm.nFrmLeft;

							// params
							byte n_layers = dis.readByte();
//                     		frm.shLayers[0] = new short[n_layers];
//                     		frm.shLayers[1] = new short[n_layers];
//                     		frm.shLayers[2] = new short[n_layers];
//                     		frm.shLayers[3] = new short[n_layers];
							frm.shLayers = new long[n_layers];

							frm.bDelay = dis.readByte();
							frm.nDx = dis.readShort();
							frm.nDy = dis.readShort();

							// points
							byte b_opt = dis.readByte();
							frm.bOpt = b_opt;
							if ((b_opt & 1) != 0) {
								frm.pntX_1 = dis.readByte();
								frm.pntY_1 = dis.readByte();
							}

							if ((b_opt & 2) != 0) {
								frm.pntX_2 = dis.readByte();
								frm.pntY_2 = dis.readByte();
							}

							// layers
							for (int la = 0; la < n_layers; la++ ) {
								short n_pic_id = dis.readShort();
								short n_ldx = dis.readShort();
								short n_ldy = dis.readShort();

//                       		frm.shLayers[0][la] = n_pic_id;
//                        		frm.shLayers[1][la] = n_ldx;
//                        		frm.shLayers[2][la] = n_ldy;
//                        		frm.shLayers[3][la] = 1;
								frm.shLayers[la] = (((long)n_pic_id << 48) & 0xFFFF000000000000L) |
													(((long)n_ldx << 32) & 0x0000FFFF00000000L) |
													(((long)n_ldy << 16) & 0x00000000FFFF0000L) |
													(1L & 0x000000000000FFFFL);
							}
						}
					}
				}
				dis.close();
			}
		}
		catch( Exception e ) {
			System.out.println( e.toString() );
		}
		finally {
			dis = null;
			System.gc();
		}
	}
}
